const chokidar = require('chokidar');
const bodyParser = require('body-parser');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const { validationUtils } = require('./utils');

const mockDir = path.join(process.cwd(), 'mock');

function registerRoutes(app) {
  let mockLastIndex;
  const mocks = requireMocks();
  const mocksForServer = mocks.map(route => {
    return responseFake(
      route.url,
      route.type,
      route.response,
      route.validation
    );
  });
  for (const mock of mocksForServer) {
    app[mock.type](mock.url, mock.response);
    mockLastIndex = app._router.stack.length;
  }
  const mockRoutesLength = Object.keys(mocksForServer).length;
  return {
    mockRoutesLength: mockRoutesLength,
    mockStartIndex: mockLastIndex - mockRoutesLength
  };
}

function unregisterRoutes() {
  Object.keys(require.cache).forEach(i => {
    if (i.includes(mockDir)) {
      delete require.cache[require.resolve(i)];
    }
  });
}

// for mock server
const responseFake = (url, type, respond, validation) => {
  return {
    // url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
    url: url,
    type: type || 'get',
    response(req, res) {
      console.log('request invoke:' + req.path);
      let { params } = req.body;
      const { isSend, msg, missParams } = validationUtils(params, validation);
      if (isSend) {
        res.send(respond(req, res));
      } else {
        if (missParams && missParams.length) {
          res.send({
            msg,
            missParams
          });
        } else {
          res.send({
            msg
          });
        }
      }
    }
  };
};

const requireMocks = () => {
  // 自动引入modules文件下下的文件
  let mockFiles = fs.readdirSync('./mock/modules');
  let mocks = [];
  mockFiles.forEach(file => {
    let module = require(`./modules/${file}`);
    if (module instanceof Array) mocks.push(...module);
  });
  return mocks;
};

module.exports = (middlewares, devServer) => {
  if (!devServer) {
    throw new Error('webpack-dev-server is not defined');
  }
  let app = devServer.app;
  app.use(bodyParser.json());
  app.use(
    bodyParser.urlencoded({
      extended: true
    })
  );

  const mockRoutes = registerRoutes(app);
  let mockRoutesLength = mockRoutes.mockRoutesLength;
  let mockStartIndex = mockRoutes.mockStartIndex;

  // watch files, hot reload mock server
  chokidar
    .watch(mockDir, {
      ignored: /mock-server/,
      ignoreInitial: true
    })
    .on('all', (event, path) => {
      if (event === 'change' || event === 'add') {
        try {
          // remove mock routes stack
          app._router.stack.splice(mockStartIndex, mockRoutesLength);

          // clear routes cache
          unregisterRoutes();

          const mockRoutes = registerRoutes(app);
          mockRoutesLength = mockRoutes.mockRoutesLength;
          mockStartIndex = mockRoutes.mockStartIndex;

          console.log(
            chalk.magentaBright(
              `\n > Mock Server hot reload success! changed  ${path}`
            )
          );
        } catch (error) {
          console.log(chalk.redBright(error));
        }
      }
    });
  return middlewares;
};
